﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MathNet.Numerics;

namespace DiffusionAnalyticalSolution
{
    public class Analytic
    {
        private double _a;
        private double _r0;
        private double _lambda;
        private Dictionary<double, double> _fractionsHash;
        
        public static int _numberOfTimesHitToHash { get; set; }

        #region define for faster debug mode execution
        private double _aOverr0;
        private double _r0Minusa;
        private double _fourD;
        private double _lambdaOverD;
        private double _NLambda;
        #endregion

        public Analytic(double a, double r0, double D)
        {
            _aOverr0 = a / r0;
            _r0Minusa = r0 - a;
            _fourD = 4 * D;

            _fractionsHash = new Dictionary<double, double>();
            _numberOfTimesHitToHash = 0;
        }

        public Analytic(double a, double r0, double D, double lambda)
        {
            _a = a;
            _lambda = lambda;
            _r0 = r0;            
            _aOverr0 = a / r0;
            _r0Minusa = r0 - a;
            _fourD = 4 * D;
            _lambdaOverD = lambda / D;
            _NLambda = (_aOverr0) * Math.Exp(-1 * Math.Sqrt(_lambdaOverD) * _r0Minusa);
            _fractionsHash = new Dictionary<double, double>();

            _numberOfTimesHitToHash = 0;
        }

        public double FractionOfMoleculesHitWithDecompositionConsidered(double t)
        {
            if (_fractionsHash.ContainsKey(t))
            {
                _numberOfTimesHitToHash++;
                return _fractionsHash[t];
            }

            var a = _NLambda - (_a / (2 * _r0)) * Math.Exp(-1 * Math.Sqrt(_lambdaOverD) * (_r0Minusa)) * (SpecialFunctions.Erf(((_r0Minusa) / Math.Sqrt(_fourD * t)) - Math.Sqrt(_lambda * t)) + 
                Math.Exp(2 * Math.Sqrt(_lambdaOverD) * (_r0Minusa)) * (SpecialFunctions.Erf(((_r0Minusa) / Math.Sqrt(_fourD * t)) + Math.Sqrt(_lambda * t)) - 1) + 1);

            _fractionsHash.Add(t, a);

            return a;
        }

        public double FractionOfMoleculesHit(double t)
        {
            if (_fractionsHash.ContainsKey(t))
            {
                return _fractionsHash[t];
            }
            
            var a = (_aOverr0) * SpecialFunctions.Erfc((_r0Minusa) / Math.Sqrt(_fourD * (t)));

            _fractionsHash.Add(t, a);

            return a;
        }

        public double DifferenceOfMolecules(double t1, double t2)
        {
            return FractionOfMoleculesHit(t2) - FractionOfMoleculesHit(t1);
        }

        public double DifferenceOfMoleculesWithDecompositionConsidered(double t1, double t2)
        {
            return FractionOfMoleculesHitWithDecompositionConsidered(t2) - FractionOfMoleculesHitWithDecompositionConsidered(t1);
        }

        public static double FractionOfMoleculesHit(double a, double r0, double D, double t, double t0)
        {
            return (a / r0) * SpecialFunctions.Erfc((r0 - a) / Math.Sqrt(4 * D * (t - t0)));
        }

        public static double DifferenceOfMolecules(double a, double r0, double D, double t2, double t1)
        {
            return FractionOfMoleculesHit(a, r0, D, t2, 0) - FractionOfMoleculesHit(a, r0, D, t2, 0);
        }

    }
}
